﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Se.Config;
using System.Collections.Concurrent;
using System.Threading;
using Se.Log;
using Se.Common;

namespace Se.Net.Sockets
{
    /// <summary>
    /// Socket Listener
    /// </summary>
    public class SocketListener
    {
        #region Members

        private Socket _socket;
        private ConcurrentDictionary<string, ExSocket> _clientSockets;
        private ConcurrentStack<SocketAsyncEventArgs> _sendArgsPool;
        private ConcurrentStack<SocketAsyncEventArgs> _receiveArgsPool;
        private ConcurrentStack<SocketAsyncEventArgs> _acceptArgsPool;
        private SocketSettings _setting;
        private BufferManager _bufferManager;
        private Semaphore _maxConnectionsEnforcer;
        /// <summary>
        /// ip黑名单
        /// </summary>
        private HashSet<long> _blackIPList;

        #endregion

        #region event

        public event Action<ExSocket> SocketConnected;
        private void OnSocketConnected(ExSocket acceptedSocket)
        {
            if (this.SocketConnected != null)
                this.SocketConnected(acceptedSocket);
        }

        public event Action<ExSocket> SocketDisConnected;
        private void OnSocketDisConnected(ExSocket socket)
        {
            if (this.SocketDisConnected != null)
                this.SocketDisConnected(socket);
        }

        public event Action<SocketReceivedArgs> SocketReceived;
        private void OnSocketReceived(SocketReceivedArgs args)
        {
            if (this.SocketReceived != null) this.SocketReceived(args);
        }

        #endregion

        public SocketListener(SocketSettings socketSetting)
        {
            this._blackIPList = new HashSet<long>();
            this._setting = socketSetting;
            this._clientSockets = new ConcurrentDictionary<string, ExSocket>();
            this._bufferManager = new BufferManager();
            this._bufferManager.Init(socketSetting.BufferSize * socketSetting.MaxConnections * 2, socketSetting.BufferSize);
            this._sendArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();
            this._receiveArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();
            for (int i = 0; i < socketSetting.MaxConnections; i++)
            {
                var receiveArgs = new SocketAsyncEventArgs();
                receiveArgs.Completed += Receive_Completed;
                this._receiveArgsPool.Push(receiveArgs);

                var sendArgs = new SocketAsyncEventArgs();
                sendArgs.Completed += Send_Completed;
                this._sendArgsPool.Push(sendArgs);
            }
            var tempArgsPool = this._sendArgsPool.ToList();
            tempArgsPool.AddRange(this._receiveArgsPool);
            this._bufferManager.SetBuffer(tempArgsPool);
            this._acceptArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();
            for (var i = 0; i < socketSetting.NumOfSaeaForRecSend; i++)
            {
                var args = new SocketAsyncEventArgs();
                args.Completed += Accept_Completed;
                this._acceptArgsPool.Push(args);
            }
            this._maxConnectionsEnforcer = new Semaphore(socketSetting.MaxConnections, socketSetting.MaxConnections);

            this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                this._socket.Bind(socketSetting.LocalEndPoint);
            }
            catch (SocketException ex)
            {
                SeLog.Error("此端口号已经被使用");
                throw ex;
            }
        }

        /// <summary>
        /// 开始
        /// </summary>
        public void StartListen()
        {
            this._socket.Listen(_setting.Backlog);

            ProcessAccept();
        }

        #region Accept

        /// <summary>
        /// 接受请求
        /// </summary>
        private void ProcessAccept()
        {
            // 集合中取出listenargs，连接成功
            SocketAsyncEventArgs listenArgs;
            if (!this._acceptArgsPool.TryPop(out listenArgs))
            {
                SeLog.Error("SocketListener->ProcessAccept 等待连接数量已满，无法创建新的连接");
                return;
            }
            var willRaiseEvent = this._socket.AcceptAsync(listenArgs);
            if (!willRaiseEvent)
                Accept_Completed(null, listenArgs);
        }

        /// <summary>
        /// 接受请求处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void Accept_Completed(object sender, SocketAsyncEventArgs args)
        {
            ProcessAccept();

            if (args.SocketError == SocketError.Success)
            {
                var clientSocket = args.AcceptSocket;
                // 连接完成，将args放回连接池中
                args.AcceptSocket = null;
                this._acceptArgsPool.Push(args);

                // 判断连接是否在黑名单中
                var clientEndPoint = clientSocket.RemoteEndPoint as IPEndPoint;
                if (clientEndPoint == null)
                {
                    CloseSocket(clientSocket);
                    return;
                }
                if (this._blackIPList.Contains(clientEndPoint.Address.Address))
                {
                    CloseSocket(clientSocket);
                    return;
                }

                SocketAsyncEventArgs receiveAsyncArgs;
                if (!this._receiveArgsPool.TryPop(out receiveAsyncArgs))
                {
                    SeLog.Error("SocketListener->Accept_Completed,can't pop from receiveArgsPool,pool capacity is {0}", this._receiveArgsPool.Count);
                    CloseSocket(clientSocket);
                    return;
                }
                receiveAsyncArgs.AcceptSocket = clientSocket;
                SocketAsyncEventArgs sendAsyncArgs;
                if (!this._sendArgsPool.TryPop(out sendAsyncArgs))
                {
                    SeLog.Error("SocketListener->Accept_Completed,can't pop from sendArgsPool,pool capacity is {0}", this._sendArgsPool.Count);
                    CloseSocket(clientSocket);
                    return;
                }
                sendAsyncArgs.AcceptSocket = clientSocket;
                ExSocket acceptedSocket = new ExSocket(receiveAsyncArgs.AcceptSocket);
                acceptedSocket.receiveAsyncArgs = receiveAsyncArgs;
                acceptedSocket.SendAsyncArgs = sendAsyncArgs;
                acceptedSocket.EndPoint = clientEndPoint;
                var receiveToken = new UserToken(Guid.NewGuid().ToString(), acceptedSocket);
                var sendToken = new UserToken(Guid.NewGuid().ToString(), acceptedSocket);
                receiveAsyncArgs.UserToken = receiveToken;
                sendAsyncArgs.UserToken = sendToken;
                this._clientSockets[acceptedSocket.Key] = acceptedSocket;

                // 连接事件
                this.OnSocketConnected(acceptedSocket);

                // 开始执行接受
                ProcessReceive(receiveAsyncArgs);
            }
        }

        #endregion

        #region Send

        public void Send(ExSocket socket, byte[] data)
        {
            if (data.Length > 0)
            {
                this.Send(socket, data, 0, data.Length);
            }
            else
            {
                SeLog.Error("SocketListner->Send Try Send empty package");
            }
        }

        /// <summary>
        /// 发送，将数据流推送到发送队列中
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="data"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public void Send(ExSocket socket, byte[] data, int offset, int count)
        {
            // 封包
            var sendTitle = BitConverter.GetBytes(count);
            var sendBytes = new byte[count + sendTitle.Length];
            Buffer.BlockCopy(sendTitle, 0, sendBytes, 0, sendTitle.Length);
            Buffer.BlockCopy(data, offset, sendBytes, sendTitle.Length, count);
            socket.Enqueue(sendBytes);
            if (socket.TrySetSendFlag())
            {
                TryDequeueAndSend(socket);
            }
        }

        /// <summary>
        /// 执行Send
        /// </summary>
        /// <param name="socket"></param>
        private void TryDequeueAndSend(ExSocket socket)
        {
            var sendArgs = socket.SendAsyncArgs;
            byte[] data;
            if (socket.TryDequeue(out data))
            {
                socket.TrySetSendFlag();
                try
                {
                    sendArgs.SetBuffer(data, 0, data.Length);
                    //UserToken token = new UserToken(string.Empty, socket);
                    //sendArgs.UserToken = token;
                    //sendArgs.Completed += Send_Completed;
                    var willRaiseEvent = socket.Socket.SendAsync(sendArgs);
                    if (!willRaiseEvent)
                    {
                        Send_Completed(null, sendArgs);
                    }
                }
                catch
                {
                    socket.ResetSendFlag();
                }
            }
            else
            {
                socket.ResetSendFlag();
            }
        }

        /// <summary>
        /// 发送数据完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Send_Completed(object sender, SocketAsyncEventArgs args)
        {
            var usertoken = (UserToken)args.UserToken;
            if (args.SocketError != SocketError.Success)
            {
                HandleCloseSocket(usertoken.ExSocket);
                return;
            }

            UserToken token = (UserToken)args.UserToken;
            TryDequeueAndSend(token.ExSocket);
        }

        #endregion

        #region Receive

        /// <summary>
        /// 接收数据完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Receive_Completed(object sender, SocketAsyncEventArgs args)
        {
            var token = (UserToken)args.UserToken;

            if (args.SocketError != SocketError.Success)
            {
                this.HandleCloseSocket(token.ExSocket);
                return;
            }
            if (args.BytesTransferred == 0)
            {
                this.HandleCloseSocket(token.ExSocket);
                return;
            }

            // 解析数据
            var doneBytes = 0;
            while ((args.BytesTransferred - doneBytes) > 4)
            {
                byte[] byteTitle = new byte[4];
                Buffer.BlockCopy(args.Buffer, args.Offset + doneBytes, byteTitle, 0, 4);
                doneBytes += 4;
                var byteLength = BitConverter.ToInt32(byteTitle, 0);
                if (args.BytesTransferred < byteLength + doneBytes)
                {
                    SeLog.Error("SocketListener->Receive_Completed 长度错误");
                    HandleCloseSocket(token.ExSocket);
                    return;
                }
                SocketReceivedArgs receiveArgs = new SocketReceivedArgs() { Socket = token.ExSocket, Data = new byte[byteLength] };
                Buffer.BlockCopy(args.Buffer, args.Offset + doneBytes, receiveArgs.Data, 0, byteLength);
                this.OnSocketReceived(receiveArgs);
                doneBytes += byteLength;
            }
            ProcessReceive(args);
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        private void ProcessReceive(SocketAsyncEventArgs receiveArgs)
        {
            var willRaiseEvent = receiveArgs.AcceptSocket.ReceiveAsync(receiveArgs);
            if (!willRaiseEvent)
                this.Receive_Completed(null, receiveArgs);
        }

        #endregion

        private void HandleCloseSocket(ExSocket exSocket)
        {
            //关闭Socket
            this.CloseSocket(exSocket.Socket);
            //回收asyncargs
            this._sendArgsPool.Push(exSocket.SendAsyncArgs);
            this._receiveArgsPool.Push(exSocket.receiveAsyncArgs);
            // 触发事件
            this.OnSocketDisConnected(exSocket);

        }

        /// <summary>
        /// 只断开连接，关闭Socket
        /// </summary>
        /// <param name="socket"></param>
        private void CloseSocket(Socket socket)
        {
            socket.Disconnect(false);
            // 不需触发断开连接事件，此种情况为只创建连接，没有数据通信时调用
        }
    }
}
